package com.hrms.hrms_security.component;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.hrms.common.utils.JwtUtils;
import com.hrms.hrms_security.entity.SysUsr;
import com.hrms.hrms_security.service.SysUsrService;

import com.hrms.common.utils.RedisUtil;
import com.hrms.hrms_security.vo.AccountUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

/**
 * 登录校验器
 *
 * JWT过滤器JwtAuthenticationFilter，当前端发来的请求有JWT信息时，该过滤器将检验JWT是否正确以及是否过期，
 *
 * 若检验成功，则获取JWT中的用户名信息，检索数据库获得用户实体类，并将用户信息告知Spring Security，
 * 后续我们就能调用security的接口获取到当前登录的用户信息。
 *
 * 若前端发的请求不含JWT，我们也不能拦截该请求，因为一般的项目都是允许匿名访问的，有的接口允许不登录就能访问，没有JWT也放行是安全的，
 * 因为我们可以通过Spring Security进行权限管理，设置一些接口需要权限才能访问，不允许匿名访问
 *
 */
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Resource
    JwtUtils jwtUtils;
    @Resource
    RedisUtil redisUtil;

    @Resource
    SysUsrService sysUsrService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String jwt = request.getHeader("token");
        // 这里如果没有jwt，继续往后走，因为后面还有鉴权管理器等去判断是否拥有身份凭证，所以是可以放行的
        // 没有jwt相当于匿名访问，若有一些接口是需要权限的，则不能访问这些接口
        if (StrUtil.isBlankOrUndefined(jwt)) {
            chain.doFilter(request, response);
            return;
        }
        //TODO 令牌刷新机制

        Claims claim = null;
        try {
            claim = jwtUtils.parseJWT(jwt);
            if (claim == null) {
                throw new JwtException("token 异常");
            }
            if (jwtUtils.isTokenExpired(claim)) {
                throw new JwtException("token 已过期");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

        String username = claim.getSubject();
        // 获取用户的权限等信息
        try {
            String LoginUserString = redisUtil.get("login." + username);
            AccountUser LoginUser = JSON.parseObject(LoginUserString, AccountUser.class);
            if (Objects.isNull(LoginUser)) {
                throw new JwtException("token 异常");
            }
            // 构建UsernamePasswordAuthenticationToken,这里密码为null，是因为提供了正确的JWT,实现自动登录
            //获取权限信息
            SysUsr sysUser = sysUsrService.getByUsername(username);
            String authorityInfo = sysUsrService.getUserAuthorityInfo(sysUser.getId());

            UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(LoginUser, null, AuthorityUtils.commaSeparatedStringToAuthorityList(authorityInfo));
            SecurityContextHolder.getContext().setAuthentication(token);
        }catch (Exception e) {
            e.printStackTrace();
        }

        chain.doFilter(request, response);

    }
}
